/*
23 * Predictor3.cpp
 *
 *  Created on: Dec 8, 2014
 *      Author: ale
 */

#include "Predictor3.h"

Predictor3::Predictor3() {
	unigramas = new VectorUnigramas;
	bigramas = new VectorBigramas(unigramas);
	trigramas = new VectorTrigramas(bigramas);

	Aanterior = "";
	Banterior = "";
	offsetActual = 0;

	bigramasConAigual = vector<string>();
	probConAigual = vector<double>();

	trigramasConABiguales = vector<string>();
	probConABiguales = vector<double>();
}

Predictor3::~Predictor3() {
	// TODO Auto-generated destructor stub
}

long double Predictor3::obtenerProb(string ABC){

	long double prob;

	boost::char_separator<char> sep(" ");

	boost::tokenizer< boost::char_separator<char> > tokens(ABC, sep);

	string A, B, C;
	boost::tokenizer< boost::char_separator<char> >::iterator i = tokens.begin();

	A = i.current_token();
	i++;
	B = i.current_token();
	i++;
	C = i.current_token();

	long double uniTerm = this->obtenerProbTermUnigrama(C, B);
	long double biProb = this->obtenerProbBigrama(B,C);
	long double biBoff = this->obtenerBoffBigrama(A,B);
	long double triProb = this->obtenerProbTermTrigrama(A,B,C);
	long double aux1;
	long double aux2;

	if (biBoff != 0){
		aux1 = biProb + uniTerm;
		aux2 = biBoff * aux1;
		prob = triProb +  aux2;
	}else
		prob = uniTerm;
/*
	cout << ABC << " prob--> ";
	cout << triProb << " + " << biBoff << " * (" << biProb << " + " << uniTerm << ") = ";
	cout << prob <<endl;
*/

	return prob;
/*

	if (triProb != 0)
		return triProb;
	if ((biBoff != 0) && (biProb != 0))
		return biBoff * biProb;

	return uniTerm;
*/
}

/*Devuelve la probabilidad de C y el backoff de B multiplicados*/
long double Predictor3::obtenerProbTermUnigrama(string C, string B){
	long double UniTerm;
	double logprob;
	double logboff;
	long double logUniTerm;
	logprob = unigramas->obtenerProbabilidad(C);
	logboff = unigramas->obtenerBackOff(B);

	if (logprob == 0)
		return 0;

	logUniTerm = logprob + logboff;
	UniTerm = pow(10,logUniTerm);
	return UniTerm;
}

/*Devuelve la probabilidad del bigrama BC o 0 si no esta*/
long double Predictor3::obtenerProbBigrama(string B, string C){
	long double BiProb;
	double logprob;

	logprob = bigramas->obtenerProbabilidad(B,C);

	if (logprob == 0)
		return 0;
	BiProb = pow(10,logprob);
	/*
	if (BiProb >= 1){
		cout << "Hola soy " << B+" "+C << " Prob--> "<< BiProb << ", logProb--> "<< logprob;
		cout << endl;
	}*/

	return BiProb;
}

/*Devuelve el backoff del bigrama AB o 0 si no esta*/
long double Predictor3::obtenerBoffBigrama(string A, string B){
	long double BiBoff;
	double logboff;
	logboff = bigramas->obtenerBackOff(A,B);

	if (logboff == 0)
		return 0;

	BiBoff = pow(10,logboff);

	/*
	if (BiBoff >= 1){
		cout << "Hola soy " << A+" "+B << " Boff--> "<< BiBoff << ", logBoff--> "<< logboff;
		cout << endl;
	}*/
	return BiBoff;
}

//devuelve 0 si no esta el trigrama
long double Predictor3::obtenerProbTermTrigrama(string A, string B, string C){
	long double prob;
	long double logprob;
	logprob = trigramas->obtenerProbabilidad(A,B,C);

	if (logprob == 0)
		return 0;

	prob = pow(10,logprob);

	/*if (prob >= 1){
		cout << "Hola soy " << A+" "+B+" "+C << " Prob--> "<< prob << ", logProb--> "<< logprob;
		cout << endl;
	}*/

	return prob;
}


bool Predictor3::agregarUnigrama(string uni, double prob, double boff){
	unigramas->agregarUnigrama(uni, prob, boff);
	return true;
}

bool Predictor3::agregarBigrama(string bigrama, double prob, double boff, istream::streampos offsetInicioDeLinea){
	boost::char_separator<char> sep(" ");
	boost::tokenizer< boost::char_separator<char> > tokens(bigrama, sep);

	string A, B;
	boost::tokenizer< boost::char_separator<char> >::iterator i = tokens.begin();

	A = i.current_token();
	i++;
	B = i.current_token();

	bigramas->agregarBigrama(A, B, prob, boff);

	//AGREGO EL OFFSET DE LOS BIGRAMAS QUE EMPIEZAN CON EL MISMO UNIGRAMA

	if (Aanterior == ""){ //caso inicial
		Aanterior = A;
		bigramasConAigual.push_back(B);
		probConAigual.push_back(prob);
		unigramas->agregarOffsetDeAenBigramas(A,offsetInicioDeLinea);
		return true;
	}

	if ((A == Aanterior)){
		bigramasConAigual.push_back(B);
		probConAigual.push_back(prob);
		return true;
	}else{
		int indexMejor = NgramaMasProbable(&probConAigual);
		if (indexMejor > -1){
			unigramas->agregarMejorBigramaQueCompleta(Aanterior, bigramasConAigual[indexMejor]);
		}

		unigramas->agregarOffsetDeAenBigramas(A,offsetInicioDeLinea);
		Aanterior = A;
		bigramasConAigual.clear();
		probConAigual.clear();
		bigramasConAigual.push_back(B);
		probConAigual.push_back(prob);
	}

	return true;
}

bool Predictor3::agregarTrigrama(string trigrama, double prob, istream::streampos offsetInicioDeLinea){
	boost::char_separator<char> sep(" ");
	boost::tokenizer< boost::char_separator<char> > tokens(trigrama, sep);

	string A, B, C;
	boost::tokenizer< boost::char_separator<char> >::iterator i = tokens.begin();

	A = i.current_token();
	i++;
	B = i.current_token();
	i++;
	C = i.current_token();

	trigramas->agregarTrigrama(A,B,C,prob);

	//AGREGO EL OFFSET DE LOS TRIGRAMAS QUE EMPIEZAN CON EL MISMO BIGRAMA

	if (Banterior == ""){ //el primer trigrama
		Aanterior = A;
		Banterior = B;
		trigramasConABiguales.push_back(C);
		probConABiguales.push_back(prob);
		bigramas->agregarOffsetDeABenTrigramas(A,B,offsetInicioDeLinea);
		return true;
	}

	if ((A == Aanterior) && (B == Banterior)){
		//agregar a vector de iguales
		trigramasConABiguales.push_back(C);
		probConABiguales.push_back(prob);
		//cout << "IGUAL AL AB ANTERIOR --> " << trigrama << endl;
		return true;
	}else{
		//calcular el mejor de los anteriores, guardarlo y hacer un vector nuevo con este nuevo trigrama
		int indexMejor = NgramaMasProbable(&probConABiguales);
		if (indexMejor > -1){
			bigramas->agregarMejorTrigramaQueCompleta(Aanterior,Banterior, trigramasConABiguales[indexMejor]);
		}
		//cout << endl << endl;
		//cout << "CAMBIO DE AB --> " << trigrama << endl;
		bigramas->agregarOffsetDeABenTrigramas(A,B,offsetInicioDeLinea);
		Aanterior = A;
		Banterior = B;
		trigramasConABiguales.clear();
		probConABiguales.clear();
		trigramasConABiguales.push_back(C);
		probConABiguales.push_back(prob);

	}
	return true;
}

int Predictor3::NgramaMasProbable(vector<double> *probas){
	for(unsigned int i = 0; i< probas->size(); i++)
			(*probas)[i] = pow(10,(*probas)[i]);

	double mayorProb = 0;
	int index = -1;

	for(unsigned int i = 0; i< probas->size(); i++){
		if ((*probas)[i] > mayorProb){
			mayorProb =(*probas)[i];
			index = i;
		}
	}
	return index;
}
